package cn.sc.summer.mybatis.config.interceptor;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.sc.summer.mybatis.annotation.encrypt.EncryptedField;
import cn.sc.summer.mybatis.annotation.encrypt.EncryptedTable;
import com.baomidou.mybatisplus.core.metadata.IPage;
import cn.sc.summer.constant.encrypt.EncryptKeyConstant;
import cn.sc.summer.constant.seneitive.DesensitizedUtil;
import cn.sc.summer.constant.seneitive.Sensitive;
import cn.sc.summer.constant.seneitive.SensitiveTypeEnum;
import cn.sc.summer.token.util.AESUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.List;
import java.util.Objects;

/**
 * 类名：解密拦截器
 *
 * @author a-xin
 * @date 2024/5/30 09:36
 */
@Slf4j
@Intercepts({
        @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
@Component
public class DecryptInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object resultObject = invocation.proceed();
        if (Objects.isNull(resultObject)) {
            return invocation.proceed();
        }
        //分页数据返回
        if (resultObject instanceof IPage) {
            IPage<?> page = (IPage<?>) resultObject;
            if (CollUtil.isNotEmpty(page.getRecords()) && needToDecrypt(page.getRecords().get(0))) {
                for (Object result : page.getRecords()) {
                    decrypt(result);
                }
            }
            //集合数据返回
        } else if (resultObject instanceof List<?>) {
            List<?> resultList = (List<?>) resultObject;
            if (CollUtil.isNotEmpty(resultList) && needToDecrypt(resultList.get(0))) {
                for (Object result : resultList) {
                    decrypt(result);
                }
            }
            //单个数据返回
        } else if (needToDecrypt(resultObject)) {
            decrypt(resultObject);
        }
        return resultObject;
    }

    /**
     * 校验该实例的类是否被@EncryptedTable所注解
     *
     * @param object 返回对象
     * @return 是都需要解密
     */
    private boolean needToDecrypt(Object object) {
        Class<?> objectClass = object.getClass();
        EncryptedTable sensitiveData = AnnotationUtils.findAnnotation(objectClass, EncryptedTable.class);
        return ObjectUtil.isNotNull(sensitiveData);
    }

    @Override
    public Object plugin(Object o) {
        if (o instanceof ResultSetHandler) {
            return Plugin.wrap(o, this);
        } else {
            return o;
        }
    }

    /**
     * 进行数据解密和脱敏处理
     *
     * @param result 返回结果
     * @param <T>    结果类型
     * @throws Exception 处理异常
     */
    private <T> void decrypt(T result) throws Exception {

        Class<?> resultClass = result.getClass();
        Field[] declaredFields = ReflectUtil.getFields(resultClass);
        for (Field field : declaredFields) {

            //取出所有被EncryptedField注解的字段
            EncryptedField encryptedField = field.getAnnotation(EncryptedField.class);
            if (ObjectUtil.isNotNull(encryptedField)) {
                field.setAccessible(true);
                Object object = field.get(result);
                //只支持String的解密
                if (object instanceof String) {
                    String value = (String) object;
                    //对注解的字段进行逐一解密
                    String results = AESUtil.decrypt(value, EncryptKeyConstant.DATASOURCE_AES_SECURE_KEY);
                    field.set(result, StrUtil.isNotBlank(results) ? results : value);
                }
            }

            //取出所有被Sensitive注解的字段
            Sensitive sensitiveField = field.getAnnotation(Sensitive.class);
            if (ObjectUtil.isNotNull(sensitiveField)) {
                field.setAccessible(true);
                Object object = field.get(result);
                //只支持String的解密
                if (object instanceof String) {
                    String value = (String) object;
                    //对需要脱敏的数据进行处理
                    String results;
                    SensitiveTypeEnum sensitiveTypeEnum = sensitiveField.type();
                    switch (sensitiveTypeEnum) {
                        case CUSTOMER:
                            results = DesensitizedUtil.desValue(value, sensitiveField.prefixNoMaskLen(),
                                    sensitiveField.suffixNoMaskLen(),
                                    sensitiveField.symbol());
                            break;
                        case NAME:
                            results = DesensitizedUtil.chineseName(value);
                            break;
                        case ID_NUM:
                            results = DesensitizedUtil.idCardNum(value);
                            break;
                        case PHONE_NUM:
                            results = DesensitizedUtil.mobilePhone(value);
                            break;
                        default:
                            throw new RuntimeException("Unknown the sensitive type enum : " + sensitiveTypeEnum);
                    }
                    field.set(result, StrUtil.isNotBlank(results) ? results : value);
                }
            }

        }
    }

}
